/* AndyDroid - Android automation testing Framework.
 Copyright (C) 2011 MicroStrategy Test Automation Team

 AndyDroid is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with software. If not, contact us.
 
 @email:	robort1002@gmail.com
 */
package com.microstrategy.android.automation.view;

import junit.framework.Assert;
import android.app.Instrumentation;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

/**
 * 
 * 
 * 
 */
public class ViewController {
	private static final String LOG_TAG = "ViewOperation";
	private Instrumentation inst;
	private static ViewController instance;

	public ViewController(Instrumentation inst) {
		this.inst = inst;
	}
	
	static ViewController getInstance(Instrumentation inst) {
		if (null == instance) {
			instance = new ViewController(inst);
		}
		return instance;
	}

	/**
	 * Simulate touching a specific location and dragging to a new location.
	 * 
	 * This method was copied from {@code TouchUtils.java} in the Android Open
	 * Source Project, and modified here.
	 * 
	 * @param fromX
	 *            X coordinate of the initial touch, in screen coordinates
	 * @param toX
	 *            Xcoordinate of the drag destination, in screen coordinates
	 * @param fromY
	 *            X coordinate of the initial touch, in screen coordinates
	 * @param toY
	 *            Y coordinate of the drag destination, in screen coordinates
	 * @param stepCount
	 *            How many move steps to include in the drag
	 * 
	 */
	public void drag(float fromX, float toX, float fromY, float toY,
			int stepCount) {
		long downTime = SystemClock.uptimeMillis();
		long eventTime = SystemClock.uptimeMillis();
		float y = fromY;
		float x = fromX;
		float yStep = (toY - fromY) / stepCount;
		float xStep = (toX - fromX) / stepCount;
		MotionEvent event = MotionEvent.obtain(downTime, eventTime,
				MotionEvent.ACTION_DOWN, fromX, fromY, 0);
		try {
			inst.sendPointerSync(event);
		} catch (SecurityException ignored) {
		}
		inst.waitForIdleSync();
		
		for (int i = 0; i < stepCount; ++i) {
			y += yStep;
			x += xStep;
			eventTime = SystemClock.uptimeMillis();
			event = MotionEvent.obtain(downTime, eventTime,
					MotionEvent.ACTION_MOVE, x, y, 0);
			try {
				inst.sendPointerSync(event);
			} catch (SecurityException ignored) {
			}
			
			inst.waitForIdleSync();
		}
		
		eventTime = SystemClock.uptimeMillis();
		event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
				toX, toY, 0);
		try {
			inst.sendPointerSync(event);
		} catch (SecurityException ignored) {
		}
		
		inst.waitForIdleSync();
	}

	/**
	 * Click on a given view
	 * @throws InterruptedException
	 */
	public void clickOnView(View view, boolean longClick)
			throws InterruptedException {
		int[] xy = new int[2];

		view.getLocationOnScreen(xy);

		final int viewWidth = view.getWidth();
		final int viewHeight = view.getHeight();
		final float x = xy[0] + (viewWidth / 2.0f);
		float y = xy[1] + (viewHeight / 2.0f);
		Log.i(LOG_TAG, "click on x: " + x + " y: " + y);

		if (longClick) {
			clickLongOnScreen(x, y);
		} else {
			clickOnScreen(x, y);
		}
	}

	/**
	 * Clicks on a given coordinate on the screen
	 * @param x
	 *            the x coordinate
	 * @param y
	 *            the y coordinate
	 */
	public void clickOnScreen(float x, float y) {
		long downTime = SystemClock.uptimeMillis();
		long eventTime = SystemClock.uptimeMillis();
		MotionEvent event = MotionEvent.obtain(downTime, eventTime,
				MotionEvent.ACTION_DOWN, x, y, 0);
		MotionEvent event2 = MotionEvent.obtain(downTime, eventTime,
				MotionEvent.ACTION_UP, x, y, 0);

		try {
			inst.sendPointerSync(event);
			inst.sendPointerSync(event2);
			Log.i(LOG_TAG, "sent motionevent to click, x is " + x + " y is "
					+ y);

		} catch (SecurityException e) {
			Assert.assertTrue("Click can not be completed!", false);
		}
	}

	/**
	 * Long clicks a given coordinate on the screen
	 * @param x
	 *            the x coordinate
	 * @param y
	 *            the y coordinate
	 * @throws InterruptedException
	 */
	public void clickLongOnScreen(float x, float y) throws InterruptedException {
		long downTime = SystemClock.uptimeMillis();
		long eventTime = SystemClock.uptimeMillis();
		MotionEvent event = MotionEvent.obtain(downTime, eventTime,
				MotionEvent.ACTION_DOWN, x, y, 0);
		try {
			inst.sendPointerSync(event);
		} catch (SecurityException e) {
			Assert.assertTrue(
					"Click can not be completed! Something is in the way e.g. the keyboard.",
					false);
		}
		inst.waitForIdleSync();
		eventTime = SystemClock.uptimeMillis();
		event = MotionEvent.obtain(downTime, eventTime,
				MotionEvent.ACTION_MOVE, x + ViewConfiguration.getTouchSlop()
						/ 2, y + ViewConfiguration.getTouchSlop() / 2, 0);
		inst.sendPointerSync(event);
		inst.waitForIdleSync();
		Thread.sleep((int) (ViewConfiguration.getLongPressTimeout() * 1.5f));

		eventTime = SystemClock.uptimeMillis();
		event = MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP,
				x, y, 0);
		inst.sendPointerSync(event);
		inst.waitForIdleSync();
	}
}
